package cn.edu.cqu.bysj.tool;

import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;

public class CipherUtil {

    public static PublicKey getPublicKey(){
        PublicKey key=null;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            BigInteger modulus = new BigInteger(CommonValue.CIPHER_MODULUS);
            BigInteger exponents = new BigInteger(CommonValue.CIPHER_EXPONENT);
            RSAPublicKeySpec spec=new RSAPublicKeySpec(modulus,exponents);
            key=keyFactory.generatePublic(spec);

        }   catch (InvalidKeySpecException e){
            e.printStackTrace();
        }   catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }
        return key;
    }

    public static PrivateKey getPrivateKey(){
        PrivateKey key=null;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            BigInteger modulus = new BigInteger(CommonValue.CIPHER_MODULUS);
            BigInteger exponents = new BigInteger(CommonValue.DECIPHER_EXPONENT);
            RSAPrivateKeySpec spec=new RSAPrivateKeySpec(modulus,exponents);
            key=keyFactory.generatePrivate(spec);

        }   catch (InvalidKeySpecException e){
            e.printStackTrace();
        }   catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }
        return key;
    }

    public static byte[] encryptData(String data) throws IOException {  
        byte[] dataToEncrypt = data.getBytes();
        byte[] encryptedData = null;
        try {
            PublicKey pubKey =getPublicKey();
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            encryptedData = cipher.doFinal(dataToEncrypt);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return byteToFourBits(encryptedData);
    }

    public static String decryptData(byte[] data) throws IOException{
        byte [] trueData=fourBitsToByte(data);
        byte[] descryptedData = null;
        try {
            PrivateKey privateKey = getPrivateKey();
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            descryptedData = cipher.doFinal(trueData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new String(descryptedData);

    }

    private static byte [] byteToFourBits(byte [] b){
        int lowMask=0xf;
        byte [] fourBits=new byte [b.length*2];
        for (int i=0;i<b.length;i++){

//            int curr=b[i];
            int low=b[i]&lowMask;
            fourBits[i*2+0]=CommonValue.charMap.get(low);
            int high=(b[i]>>4)&lowMask;
            fourBits[i*2+1]=CommonValue.charMap.get(high);
            ;
        }
        return fourBits;
    }

    private static byte[] fourBitsToByte(byte []f){
        assert (f.length%2==0);
//        int lowMask=0xf;
        byte [] fourBits=new byte [f.length/2];
        for (int i=0;i<fourBits.length;i++){
            int low=f[i*2+0]-97;
            int high=f[i*2+1]-97;
            high=high<<4;
            assert (low<=0xf);
            assert (high<=0xf);
            fourBits[i]=(byte)(low|high);
        }
        return fourBits;
    }
}
